package com.remote.core;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.remote.entity.FileItem;
import com.remote.framework.RestResultDto;

import ch.ethz.ssh2.ChannelCondition;
import ch.ethz.ssh2.SCPClient;
import ch.ethz.ssh2.SFTPv3Client;
import ch.ethz.ssh2.SFTPv3DirectoryEntry;
import ch.ethz.ssh2.SFTPv3FileAttributes;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;

@Service
public class RemoteCommandService extends RemoteCommandAdaptor{

	private static final Logger log = LoggerFactory.getLogger(RemoteCommandService.class);
	private static String DEFAULTCHART = "UTF-8";

	private static final String EXCULDS_ONE = ".";
	private static final String EXCULDS_TOW = "..";
	
	/**
	 * 远程执行shll脚本或者命令
	 * 
	 * @param cmd 即将执行的命令
	 * @return 命令执行完后返回的结果值
	 */
	public RestResultDto execute(String cmd) {
		String result = "";
		try {
			if (conn != null) {
				Session session = conn.openSession();// 打开一个会话
				session.execCommand(cmd);// 执行命令
				result = processStdout(session.getStdout(), DEFAULTCHART);
				// 如果为得到标准输出为空，说明脚本执行出错了
				if (StringUtils.isBlank(result)) {
					log.info("得到标准输出为空,链接conn:" + conn + ",执行的命令：" + cmd);
					result = processStdout(session.getStderr(), DEFAULTCHART);
				} else {
					log.info("执行命令成功,链接conn:" + conn + ",执行的命令：" + cmd);
				}
				conn.close();
				session.close();
			}
		} catch (IOException e) {
			log.info("执行命令失败,链接conn:" + conn + ",执行的命令：" + cmd + "  " + e.getMessage());
			e.printStackTrace();
			return RestResultDto.fail("执行命令失败,链接conn:" + conn + ",执行的命令：" + cmd + "  " + e.getMessage());
		}
		return RestResultDto.succeed("执行命令成功,链接conn:" + conn.getHostname() + ",执行的命令：" + cmd + ",执行结果：" + result);
	}

	/**
	 * 上传文件到服务器
	 */
	public boolean ftpUpload(String remoteFilePath, String localFilePath) {
		boolean bool = false;
		try {
			SCPClient scpClient = conn.createSCPClient();
			scpClient.put(localFilePath, remoteFilePath);
			bool = true;
		} catch (IOException ioe) {
			ioe.printStackTrace();
			bool = false;
		} finally {
			conn.close();
		}
		return bool;
	}

	/**
	 * 从服务器下载文件
	 */
	public boolean ftpDownload(String remoteFilePath, String localFilePath) {
		boolean bool = false;
		try {
			SCPClient scpClient = conn.createSCPClient();
			scpClient.get(remoteFilePath, localFilePath);
			bool = true;
		} catch (IOException ioe) {
			ioe.printStackTrace();
			bool = false;
		} finally {
			conn.close();
		}
		return bool;
	}

	/**
	 * Remove an File
	 */
	public boolean deleteFile(String remoteFilePath) {
		boolean bool = false;
		try {
			SFTPv3Client sftpClient = new SFTPv3Client(conn);
			sftpClient.rm(remoteFilePath);// 删除文件
			bool = true;
		} catch (IOException ioe) {
			ioe.printStackTrace();
			bool = false;
		} finally {
			conn.close();
		}
		return bool;
	}

	/**
	 * Remove an empty directory
	 */
	public boolean deleteEmptyDir(String remoteDirPath) {
		boolean bool = false;
		try {
			SFTPv3Client sftpClient = new SFTPv3Client(conn);
			sftpClient.rmdir(remoteDirPath);
			bool = true;
		} catch (IOException ioe) {
			ioe.printStackTrace();
			bool = false;
		} finally {
			conn.close();
		}
		return bool;
	}

	/**
	 * Remove an not empty directory
	 */
	public boolean deleteNotEmptyDir(String remoteDirPath) {
		boolean bool = false;
		try {
			String cmd = "rm -rf ".concat(remoteDirPath);
			execute(cmd);
			bool = true;
		} catch (Exception ioe) {
			ioe.printStackTrace();
			bool = false;
		} finally {
			conn.close();
		}
		return bool;
	}

	/**
	 * 远程传输单个文件
	 * 
	 * @param localFile
	 * @param remoteTargetDirectory
	 * @throws IOException
	 */

	public void transferFile(String localFile, String remoteTargetDirectory) throws IOException {
		File file = new File(localFile);
		if (file.isDirectory()) {
			throw new RuntimeException(localFile + "  is not a file");
		}
		String fileName = file.getAbsolutePath();
		SCPClient sCPClient = conn.createSCPClient();
		sCPClient.put(fileName, remoteTargetDirectory);
	}

	/**
	 * 传输整个目录
	 * 
	 * @param localFile
	 * @param remoteTargetDirectory
	 * @throws IOException
	 */
	public void transferDirectory(String localDirectory, String remoteTargetDirectory) throws IOException {
		File dir = new File(localDirectory);
		if (!dir.isDirectory()) {
			throw new RuntimeException(localDirectory + " is not directory");
		}

		String[] files = dir.list();
		for (String file : files) {
			if (file.startsWith(".")) {
				continue;
			}
			String fullName = localDirectory + "/" + file;
			if (new File(fullName).isDirectory()) {
				String rdir = remoteTargetDirectory + "/" + file;
				execCommand("mkdir -p " + remoteTargetDirectory + "/" + file);

				transferDirectory(fullName, rdir);
			} else {
				System.out.println("正在上传=" + fullName);
				transferFile(fullName, remoteTargetDirectory);
			}
		}

	}

	public String execCommand(String command) throws IOException {
		Session session = conn.openSession();
		session.execCommand(command);
		InputStream streamGobbler = new StreamGobbler(session.getStdout());

		String result = IOUtils.toString(streamGobbler, StandardCharsets.UTF_8);

		session.waitForCondition(ChannelCondition.EXIT_SIGNAL, Long.MAX_VALUE);

		if (session.getExitStatus().intValue() == 0) {
			log.info("execCommand: {} success ", command);
		} else {
			log.error("execCommand : {} fail", command);
		}

		IOUtils.closeQuietly(streamGobbler);
		session.close();
		return result;
	}

	/**
	 * 解析脚本执行返回的结果集
	 * 
	 * @param in      输入流对象
	 * @param charset 编码
	 * @return 以纯文本的格式返回
	 */
	private String processStdout(InputStream in, String charset) {
		InputStream stdout = new StreamGobbler(in);
		StringBuffer buffer = new StringBuffer();
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(stdout, charset));
			String line = null;
			while ((line = br.readLine()) != null) {
				buffer.append(line + "\n");
			}
		} catch (UnsupportedEncodingException e) {
			log.error("解析脚本出错：" + e.getMessage());
			e.printStackTrace();
		} catch (IOException e) {
			log.error("解析脚本出错：" + e.getMessage());
			e.printStackTrace();
		}
		return buffer.toString();
	}

	public void view(Map<String,String> connInfo,String path, HttpServletResponse response) {
		try {
			setConnInfo(connInfo);
			SCPClient scpClient = conn.createSCPClient();
			// 获取文件内容
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			scpClient.get(path, baos);
			OutputStream os = response.getOutputStream();
			byte[] btImg = baos.toByteArray();
			os.write(btImg);
			os.flush();
		} catch (IOException e) {
			log.error("没有" + path + "这个文件");
		}

	}


	public void getALLFiles(List<String> list, String dir) {
		try {
			SFTPv3Client sftpClient = new SFTPv3Client(conn);
			Vector<?> v = sftpClient.ls(dir);
			for (Object object : v) {
				SFTPv3DirectoryEntry SFTPv3DirectoryEntry = (ch.ethz.ssh2.SFTPv3DirectoryEntry) object;
				String filename = SFTPv3DirectoryEntry.filename;
				SFTPv3FileAttributes sFTPv3FileAttributes = SFTPv3DirectoryEntry.attributes;
				if (sFTPv3FileAttributes.isDirectory()
						&& !(filename.equals(EXCULDS_ONE) || filename.equals(EXCULDS_TOW))) {
					System.out.println(dir + "/" + filename);
					getALLFiles(list, dir + "/" + filename);
				} else {
					if (!(filename.equals(EXCULDS_ONE) || filename.equals(EXCULDS_TOW))) {
						list.add(dir + "/" + filename);
					}
				}

			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public List<FileItem> getFiles(Map<String,String> connInfo,String dir) {
		try {
			setConnInfo(connInfo);
			List<FileItem> list = new ArrayList<FileItem>();
			SFTPv3Client sftpClient = new SFTPv3Client(conn);
			Vector<?> v = sftpClient.ls(dir);
			for (Object object : v) {
				SFTPv3DirectoryEntry directoryEntry = (ch.ethz.ssh2.SFTPv3DirectoryEntry) object;
				String filename = directoryEntry.filename;
				SFTPv3FileAttributes sFTPv3FileAttributes = directoryEntry.attributes;
				String path = dir + "/" + filename;
				if (!EXCULDS_ONE.equals(filename)) {
					FileItem fileItem = new FileItem();
					fileItem.setName(filename);
					fileItem.setPath(path);
					fileItem.setSize(getPrintSize(sFTPv3FileAttributes.size));
					fileItem.setCreateDate(secondToDate(sFTPv3FileAttributes.mtime));
					fileItem.setAlertTime(secondToDate(sFTPv3FileAttributes.atime));
					if (sFTPv3FileAttributes.isDirectory() && !EXCULDS_TOW.equals(filename)) {
						fileItem.setIsDir(true);
						fileItem.setIsVertul(false);
						list.add(fileItem);
					} else {
						if (EXCULDS_TOW.equals(filename)) {
							fileItem.setIsDir(true);
							fileItem.setIsVertul(true);
							fileItem.setName("回到上一级");
							list.add(fileItem);
						} else {
							fileItem.setIsDir(false);
							fileItem.setIsVertul(false);
							list.add(fileItem);
						}
					}
				}
			}
			Collections.sort(list);
			return list;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	public static String getPrintSize(long size) {
		if (size <= 0)
			return "0";
		final String[] units = new String[] { "B", "kB", "MB", "GB", "TB" };
		int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
		return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
	}

	public static String secondToDate(long timeStamp) {
		String patten = "yyyy-MM-dd HH:mm:ss";
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(timeStamp * 1000);// 转换为毫秒
		Date date = calendar.getTime();
		SimpleDateFormat format = new SimpleDateFormat(patten);
		String dateString = format.format(date);
		return dateString;
	}

	public void downLoad(Map<String,String> connInfo,String path, HttpServletResponse response) {
		try {
			SCPClient scpClient = conn.createSCPClient();
			// 获取文件内容
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			scpClient.get(path, baos);
			OutputStream os = response.getOutputStream();
			String fileName = path.substring(path.lastIndexOf("/")+1);//获取要下载的文件名
			 //设置content-disposition响应头控制浏览器以下载的形式打开文件，中文文件名要使用URLEncoder.encode方法进行编码，否则会出现文件名乱码
	        response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8"));
	        byte[] btImg = baos.toByteArray();
			os.write(btImg);
			os.flush();
		} catch (IOException e) {
			log.error("没有" + path + "这个文件");
		}
	}

	public RestResultDto login(String ip, String username, String password) {
		Map<String,String> connInfo=new HashMap<String,String>();
		connInfo.put("ip", ip);
		connInfo.put("username", username);
		connInfo.put("password", password);
		setConnInfo(connInfo);
		return RestResultDto.succeed(getConn()!=null?connInfo:null);
	}

}